iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0
自我挑戰組

30天演算法解題系列 第 24

Day 24:longest peak

  • 分享至 

  • xImage
  •  

problem

輸入為一個元素皆為整數的陣列,回傳陣列中最長的山 (peak) 的長度。山是指相鄰的元素嚴格遞增到一個值最大的元素,再嚴格遞減的情況。也就是一個山最少會由三個元素組成。
舉例來說,[1, 4, 10, 2] 為山,但 [4, 0, 1]、[1, 2, 2, 0]、[2, 4, 5] 都不算是山,因為沒有遞增到最高點或在高點之後並沒有遞減下來。

sample input:
array = [1, 2, 3, 3, 4, 0, 10, 6, 5, -1, -3, 2, 3]

sample output:
6 // 0, 10, 6, 5, -1, -3

一開始的想法可能會是遍歷陣列,同時記錄每個元素之間是增還是減,並以此來找出山的結構。但這樣的作法會有很多要考慮的細節,例如陣列一開始是遞增、遞減、持平的操作就有所不同,另外也要考慮在各種找到或沒找到山的情況持續更新記錄。

第二種想法是把問題拆解成兩個部分:(1) 找出所有的山,(2) 在其中找出最長的山。

(1) 找出所有的山:山的結構一定有山頂,所以可以理解為找到所有大於左右元素的元素。作法為遍歷陣列,從第二個元素看到倒數第二個元素 (因為高點不可能是第一和最後一個數字),比較每個元素和左右相鄰的元素,並記錄高點的位置。
以範例輸入來說,遍歷之後會找到兩個山的頂點,此時只是記錄位置,還沒有計算長度。
[1, 2, 3, 3, 4, 0, 10, 6, 5, -1, -3, 2, 3]
      ^   ^
(2) 比較長度:從山頂的位置開始,向左和向右檢查,持續記錄兩邊延續的長度,直到碰到不為遞減的元素或超出陣列範圍。

實作上可以再進一步將兩個部分合在一起完成。也就是找到為山頂的數字時,直接向左右計算長度。另外,向右遞減的範圍內也可以確定不可能出現其他山頂,所以計算完後可以直接從山之後的元素繼續。例如上方例子中,從 10 開始向左右檢查,右邊 6, 5, -1, -3 這段一定不會再出現其他高點,所以計算完之後可以從 2 開始繼續步驟。

n 為陣列長度,
time: O(n)
space: O(1)

程式碼是以 while 迴圈來遍歷陣列,步驟最後計算完長度後,可以依上述所說從山結束後的位置繼續進行 (i = rightIdx 的部分)。改成用 for 迴圈檢查每一個數字也是可行。

function longestPeak(array) {
  let longestPeak = 0;
  let i = 1
  while (i < array.length - 1) {
    let isPeak = array[i] > array[i - 1] && array[i] > array[i + 1];
    if (!isPeak) {
      i++;
      continue;
    }
    // 向左右計算長度
    let leftIdx = i - 2;
    while (leftIdx >= 0 && array[leftIdx] < array[leftIdx + 1]) {
      leftIdx--;
    }
    let rightIdx = i + 2;
    while (rightIdx < array.length && array[rightIdx] < array[rightIdx - 1]) {
      rightIdx++;
    }
    // 更新目前最長的山
    let currentPeak = rightIdx - leftIdx - 1;
    longestPeak = currentPeak > longestPeak ? currentPeak : longestPeak;
    i = rightIdx;
  }
  return longestPeak;
}

上一篇
Day 23:longest palindromic substring
下一篇
Day 25:group anagrams
系列文
30天演算法解題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言